from django.db import models
from django.core.validators import *
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import *
from django.contrib import messages
from django.forms import ModelForm
from django.db.models.query import QuerySet
from django.contrib.admin import widgets 
from ocms.eventmanager.util import *
from ocms.eventmanager.gcalendar import *
import datetime
from django.utils.encoding import smart_str, smart_unicode



#Status choices
STATUS_CHOICES = (
	('U', 'Under Review'),
	('A', 'Approved'),
	('R', 'Rejected'),
	('H', 'Hold'),
)

#Event Status choices
EVENT_STATUS_CHOICES = (
	('A', 'Active'),
	('X', 'Archived'),
	('D', 'Deleted'),
)

VOTE_CHOICES = (
	('A', 'Approve'),
	('R', 'Reject'),
	('O', 'Abstain'),
)

class EventType(models.Model):
	'''
	Track the different event categories that the system will support.  
	These must line up with getForm in forms.py
	'''
	short_name = models.CharField("Short version of event name", max_length=20)
	long_name = models.CharField("Long version of event name", max_length=100)
	description = models.TextField("Description of the event type", max_length=2048)
	url = models.URLField("Link to event type", verify_exists=False, blank=True, max_length=1024)
	model_contenttype_name = models.CharField(help_text = "Must match contentype database", max_length=50)

	def __unicode__(self):
		return self.short_name
	
class EventSupportItem(models.Model):
	'''
	Standard OWASP items for events
	'''
	short_name = models.CharField("Short version of name", max_length=20)
	long_name = models.CharField("Long version of name", max_length=100)
	description = models.TextField("Description of the item", max_length=2048)

	def __unicode__(self):
		return self.short_name	

class Event(models.Model):
	'''
	This is the base class for all events.  There should be no actual objects of this type.
	'''	
	
	#This will allow us to track, at the event level, what cubclass was called
	content_type = models.ForeignKey(ContentType,editable=False,null=True, verbose_name='Event Type')

	date_created = models.DateTimeField(editable=False, auto_now_add=True, null=True)
	approval_status = models.CharField(verbose_name='Approval Status', max_length=1, default='U', choices=STATUS_CHOICES, editable="False") #event's voting status
	approval_date = models.DateTimeField(editable=False, auto_now_add=True, null=True)
	approval_status_user = models.ForeignKey(User, editable=False, null=True, blank=True)
	event_status = models.CharField(verbose_name='Active/Archive Status',max_length=1, default='A', choices=EVENT_STATUS_CHOICES, editable="False") #active/archived status flag
	event_status_date = models.DateTimeField(editable=False, auto_now_add=True, null=True)
	Name = models.CharField("Event Name", max_length=100, help_text="Proposed event name, see the wiki for naming conventions")
	Location = models.CharField("City State or Country", max_length=1000)
	host_chapter = models.CharField("Host chapter(s)", max_length=1000, blank=True)
	Venue = models.CharField("Name of Venue", blank=True, max_length=100, help_text="If multiple venue's are in review, add them in the description")
	Venue_url = models.URLField("Link to Venue", verify_exists=False, blank=True, max_length=2048)
	Start_Date = models.DateField("Start Date")
	End_Date = models.DateField("End Date")
	Website = models.URLField("Link to event specific site (non Wiki)", blank=True ,verify_exists=False, max_length=2048)
	Wiki = models.URLField("Link to event Wiki page", blank=True ,verify_exists=False, max_length=2048)
	Description = models.TextField("Description of event", max_length=2048)
	planning_team = models.ManyToManyField(User, verbose_name='Local Planning Team', related_name='+', blank=True, null=True, help_text="Core Planning team.  These users will be able to update the event details and revoke your application.  They will also be listed as official Event planners and will receive email about the event.<br><br>")
	gcal_id = models.TextField("Google Calendar event id", editable=False, null=True)
	gcc_rep = models.CharField("GCC Representative", max_length=1000, default='TBD', blank=True)
	intro_keynote = models.CharField("Introduction/Keynote", max_length=1000, default='TBD', blank=True)
	owasp_particpation = models.CharField("OWASP Committed Participation", max_length=1000, default='TBD', blank=True)
	reg_link = models.URLField("Registration Link", blank=True ,verify_exists=False, max_length=2048)
	twitter = models.CharField("Twitter", max_length=128, help_text="Event twitter account", blank=True, null=True)

	def __unicode__(self):
		return self.Name

	#updates support content typeing
	def save(self, *args, **kwargs):
		if(not self.content_type):
			self.content_type = ContentType.objects.get_for_model(self.__class__)
			super(Event, self).save(*args, **kwargs)
		
		#if all of the stuff above does not fire, save the base routine
		self.save_base()
				
		#if this is an approved event, update the calender entry if needed
		#initial entry is set in the view model		
		if ((self.approval_status=="A") and (self.gcal_id)):
			cs = GoogleCalendarWrapper()
			cs.UpdateEvent(self)
		
	#supports content typeing	
	def as_leaf_class(self):
		content_type = self.content_type
		model = content_type.model_class()
		if(model == Event):
			return self
		return model.objects.get(id=self.id)
	
	#returns a copy of this object in the correct subclass
	def cast(self):
		return self.as_leaf_class()
	
	#custom delete simply marks event status to archived
	def delete(self):
		self.event_status_date = datetime.datetime.now()
		self.event_status = 'D'
		self.save()
	
	#helper function for human readability	
	def strDates(self):
		return str(self.Start_Date) +" to "+ str(self.End_Date)
	
	#helper function to generate wiki code for event name + links	
	def strWikiName(self):
		if (self.Website and self.Wiki):
			ret_str = "["+self.Website+" "+self.Name+"] (["+self.Wiki+" Wiki])"
		elif (self.Website and not self.Wiki):
			ret_str = "["+self.Website+" "+self.Name+"]"			
		elif (self.Wiki and not self.Website):
			ret_str = "["+self.Wiki+" "+self.Name+"]"		
		else:
			ret_str = self.Name
			
		if self.reg_link:
			ret_str = ret_str +" (["+self.reg_link+" Reg])"
			
		return ret_str
	
	#helper function to identify if a user is on the planning team for this event
	def isPlanner(self, User):
			return User in self.planning_team
		
	#helper to get event url
	def strURL(self):
		return settings.SITE_URL_ROOT+"/events/"+str(self.id)
	#checks to see if sufficient votes have been placed to make a status decision
	def checkStatus(self):
		#only check if the status is undecided
		if (self.approval_status == 'U' or self.approval_status == 'H'):
			#this is critical to app functionality
			#math is 50%+1 of the number of members currently in the GCC group
			num_req = int(round(getGCCMembers().count()/2, 0))+1
			
			votes = Vote.objects.filter(event=self)
			if (votes.filter(vote="A").count() >= num_req):
				self.approve()
			elif (votes.filter(vote="R").count() >= num_req):
				self.deny()
			self.save()

	def approve(self):
		'''
		Name: 		approve(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function takes the actions involved in approving an event		
		'''	
		
		#Send email
		#update calendar
		self.approval_date=datetime.datetime.now()
		self.approval_status='A'
		
		#update event calendar
		cs = GoogleCalendarWrapper()
		gcal_event = cs.AddEvent(self)
		self.gcal_id = gcal_event.id.text
		sendEmail(self, "approve")
		
		#approve any merch requests allocated to this event
		for payment in OWASPPayment.objects.filter(event = self, balance__gte='0.0'):
			payment.order.add_status('Billed', 'Eventmanager automated approval');

		
		#save event
		self.save()
	
	def deny(self):
		'''
		Name: 		deny(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function takes the actions involved in denying an event application	
		'''	
		self.approval_date=datetime.datetime.now()
		self.approval_status='R'
		self.save()
		sendEmail(self, "deny")
		
		#approve any merch requests allocated to this event
		for payment in OWASPPayment.objects.filter(event = self, balance__gte='0.0'):
			payment.order.add_status('Cancelled', 'Eventmanager automated approval');		
		
	def hold(self, User):
		'''
		Name: 		hold(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function takes the actions involved in denying an event application	
		'''	
		self.approval_date=datetime.datetime.now()
		if self.approval_status=='U':
			self.approval_status='H'
			self.approval_status_user = User
			self.approval_date=datetime.datetime.now()
			sendEmail(self, "hold")
		else:
			self.approval_status='U'
			self.approval_status_user = None
			sendEmail(self, "unhold")
			
		self.save()
		
	
	def archive(self):
		'''
		Name: 		archive(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function takes the actions involved in archiving an event application	
		'''	
		self.event_status_date = datetime.datetime.now()
		self.event_status = 'X'
		self.save()
		
	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		content = "Name: " + self.Name + "\n"
		content += "Type: " + str(self.content_type).title() +"\n"
		content += "Registration Link: "+self.reg_link+"\n"
		content += "Details: " + settings.SITE_URL_ROOT+"/events/"+str(self.id) +"\n\n"
		content += "Venue: " + self.Venue +"\n"
		content += "Event URL: "+self.Website+ "\n"
		content += "Host Chapter: "+self.host_chapter+"\n"
		content += "Twitter: "+smart_str(self.twitter)+"\n"
		content += "\n"+ self.Description+"\n"
		
		return content
	
	def get_cost(self):
		'''
		Name: 		get_cost(self)
		Arguments: 	self = reference to myself
		Return:		Float
		Purpose: This function adds up all the costs of the event and returns the total cost	
		'''	
		
		facility_costs = float(self.facility_cost) if hasattr(self, 'facility_cost') else 0.0
		schwag_costs = float(self.schwag_cost) if hasattr(self, 'schwag_cost') else 0.0
		materials_costs = float(self.materials_cost) if hasattr(self, 'materials_cost') else 0.0
		operations_costs = float(self.operations_cost) if hasattr(self, 'operations_cost') else 0.0
		
		cost = float(facility_costs + schwag_costs + materials_costs + operations_costs)
		for payment in OWASPPayment.objects.filter(event = self):
			cost = cost + float(payment.order.total);
		
		return cost

class PromotionalEvent(Event):
	class meta:
		verbose_name = "Promotional Event"
		verbose_name_plural = "Promotional Events"
	
	organization = models.CharField("Host Organization",  max_length=100, help_text="Name of the host of the event")
	facility_cost = models.DecimalField("Cost of Booth/Table", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated (or quoted) cost of the table/booth ect slot")
	operations_cost = models.DecimalField("Depricated", editable=False, default="0.00", max_digits=19, decimal_places=2, help_text="($000.00)  Depricated")
	merch_requested = models.BooleanField("Merch Requested", default=False, help_text="Merchandise such as pens, mugs, t-shirts, stickers and books are requested for this event<br>After submitting your event, you will be taken to the OWASP Merchandise store to select your items.  At checkout please select 'OWASP EVENT' and select this event from the dropdown")
	schwag_cost = models.DecimalField("Schwag cost", editable=False, default="0.00", max_digits=19, decimal_places=2, help_text="-=DEPRICATED=- Cost of the requested standard schwag found at <a href='http://sl.owasp.org/schwag'>http://sl.owasp.org/schwag</a>.  Please enter your schwag request using the same event name as above and select 'Bill to the Global Conferences Committee'.")
	support_items = models.ManyToManyField(EventSupportItem, verbose_name='Support Items', related_name='+', blank=True, null=True, help_text="Standard OWASP event support items requested for this event<br>")
	materials = models.TextField("Additional Items", max_length=2048, help_text="Additional items requested not in standard schwag menu, please include item name, unit cost, quantity and vendor", blank=True, null=True)
	materials_cost = models.DecimalField("Additional Items Cost", default="0.00", max_digits=19, decimal_places=2, help_text="Total cost of additional items")
	shipping_address = models.TextField("Shipping Address", editable=False, blank=True, null=True, help_text="DEPRICATED Address to ship the material to (please indicate if it's your home or the venue)")
	special_instructions = models.TextField("Special Instructions", blank=True, null=True)

	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		
		content = super(PromotionalEvent, self).TxtContent()
		content += "Partner Org: " + self.organization +"\n"
		content += "Booth Cost: "+str(self.facility_cost)+ "\n"
		content += "Material Costs: "+str(self.schwag_cost+self.materials_cost)+"\n"
		content += "Standard support items: "
		for item in self.support_items.all():
			content += "\n -" + str(item)
		content += "\n"
		content += "Materials: "+str(self.materials)+"\n"
		content += "Shipping Address: "+str(self.shipping_address)+"\n"
		content += "Special Instructions: "+str(self.special_instructions)+"\n"
		content += "\n\n\n\n"+settings.SITE_URL_ROOT+"/events/"+str(self.id)
		return content
	
class PartnerEvent(Event):
	class meta:
		verbose_name = "Partner Event"
		verbose_name_plural = "Partner Events"
	
	organization = models.CharField("Partner Organization",  max_length=100, help_text="Name of the event partner")
	facility_cost = models.DecimalField("OWASP's facility costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) OWASP's Estimated cost of the facility")
	operations_cost = models.DecimalField("OWASP Operations Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) OWASP's Estimated cost for event operations, schwag, travel ...")
	partner_agreement = models.TextField("Partner Agreement", help_text="Details of the partnering agreement.  Be sure to include OWASP's obligations for the event and a description of how profits/losses will be devided.  Be advised this will need to be closely reviewed by the GCC, examples can be found <a href='https://www.owasp.org/index.php/How_to_Host_a_Conference#tab=CONTRACTS'>here</a>")
	schwag_cost = models.DecimalField("Schwag cost", default="0.00", max_digits=19, decimal_places=2, help_text="Cost of the requested standard schwag found at <a href='http://sl.owasp.org/schwag'>http://sl.owasp.org/schwag</a>.  Please enter your schwag request using the same event name as above and select 'Bill to the Global Conferences Committee'.")
	support_items = models.ManyToManyField(EventSupportItem, verbose_name='Support Items', related_name='%(app_label)s_%(class)s_related', blank=True, null=True, help_text="Standard OWASP event support items requested for this event<br>")
	materials = models.TextField("Additional Items", max_length=2048, help_text="Additional items requested not in standard schwag menu, please include item name, unit cost, quantity and vendor", blank=True, null=True)
	materials_cost = models.DecimalField("Additional Items Cost", default="0.00", max_digits=19, decimal_places=2, help_text="Total cost of additional items")
	shipping_address = models.TextField("Shipping Address", help_text="Address to ship the material to (please indicate if it's your home or the venue)", blank=True, null=True)
	special_instructions = models.TextField("Special Instructions", blank=True, null=True)
	
	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		
		content = super(PartnerEvent, self).TxtContent()
		content += "Partner Org: " + self.organization +"\n"
		content += "Facility Cost: "+str(self.facility_cost)+ "\n"
		content += "Operations Cost: "+str(self.operations_cost)+"\n"
		content += "Partner Agreement: "+self.partner_agreement+"\n"
		content += "Standard support items: "
		for item in self.support_items.all():
			content += "\n -" + str(item)
		content += "\n"
		content += "Materials: "+self.materials+"\n"
		content += "Shipping Address: "+self.shipping_address+"\n"
		content += "Special Instructions: "+str(self.special_instructions)+"\n"		
		content += "\n\n\n\n"+settings.SITE_URL_ROOT+"/events/"+str(self.id)
		return content
	
class TrainingEvent(Event):
	class meta:
		verbose_name = "Training Event"
		verbose_name_plural = "Training Events"

	facility_cost = models.DecimalField("Facility Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated (or quoted) cost of the facility for the event")
	operations_cost = models.DecimalField("Operations Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated cost for event operations, food, schwag et all")
	training_classes = models.IntegerField("Number of Training Classes", default=0, help_text="Planned number of training classes to be offered")
	training_class_size = models.IntegerField("Class Size", default=0, help_text="Estimated average number of students per training class")
	admission_fee = models.DecimalField("Training Fee", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Anticipated one day training fee")

	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		
		content = super(TrainingEvent, self).TxtContent()
		content += "Facility Cost: "+str(self.facility_cost)+ "\n"
		content += "Operations Cost: "+str(self.operations_cost)+"\n"
		content += "Training Classes: " + str(self.training_classes) +"\n"
		content += "Training Class size: " + str(self.training_class_size) +"\n"
		content += "Training Fee: "+str(self.admission_fee)+"\n"
		content += "\n\n\n\n"+settings.SITE_URL_ROOT+"/events/"+str(self.id)
		return content
	
class LocalEvent(Event):
	class meta:
		verbose_name = "Local Event"
		verbose_name_plural = "Local Events"


	facility_cost = models.DecimalField("Facility Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated (or quoted) cost of the facility for the event")
	operations_cost = models.DecimalField("Operations Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated cost for event operations, food, schwag et all")
	admission_fee = models.DecimalField("Admission Fee", default="0.00", max_digits=19, decimal_places=2, editable=False, help_text="($000.00) Anticipated 'FULL PRICE' admission cost (Whole number)")
	speaking_slots = models.IntegerField("Number of Speaking Slots", default=1, help_text="Estimated total number of speaking slots")
	tracks = models.IntegerField("Number of Tracks", default=1, editable=False, help_text="Planned number of speaking tracks at the event")

	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		
		content = super(LocalEvent, self).TxtContent()
		content += "Admission Fee: "+str(self.admission_fee)+"\n"
		content += "Facility Cost: "+str(self.facility_cost)+ "\n"
		content += "Operations Cost: "+str(self.operations_cost)+"\n"
		content += "Speaking Slots: " + str(self.speaking_slots) +"\n"
		content += "Tracks: " + str(self.tracks) +"\n"
		content += "\n\n\n\n"+settings.SITE_URL_ROOT+"/events/"+str(self.id)
		return content	
	
class RegionalEvent(Event):
	class meta:
		verbose_name = "Regional/Theme Event"
		verbose_name_plural = "Regional/Theme Events"

	facility_cost = models.DecimalField("Facility Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated (or quoted) cost of the facility for the event")
	operations_cost = models.DecimalField("Operations Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated cost for event operations, food, schwag et all")
	admission_fee = models.DecimalField("Admission Fee", default="250.00", max_digits=19, decimal_places=2, help_text="($000.00) Anticipated 'FULL PRICE' admission cost (Whole number)")
	speaking_slots = models.IntegerField("Number of Speaking Slots", default=1, help_text="Estimated total number of speaking slots. 6 per track recomended")
	tracks = models.IntegerField("Number of Tracks", default=1, help_text="Planned number of speaking tracks at the event")		
	training_classes = models.IntegerField("Number of Training Classes", default=0, help_text="Planned number of training classes to be offered")
	training_class_size = models.IntegerField("Class Size", default=0, help_text="Estimated average number of students per training class")
	training_admission_fee = models.DecimalField("Training Fee", default="750.00", max_digits=19, decimal_places=2, help_text="($000.00) Anticipated one day training fee")
	pitch_url = models.URLField("Proposal Presentation Link", blank=True, help_text="Some events choose to put together a short presentation describing the organizing committee, previous conference experience, existing local supporting organizations, local attractions, access to transportation, local culture and more.  In general, these are presentations to 'pitch' your city to the committee.  Examples of successful presentations are on the help page. <br>Can either be uploaded to the Wiki or a Google Doc (with all owasp view permissions)", verify_exists=False, max_length=2048)
	budget_url = models.URLField("Budget Link", help_text="Must be a valid link to a completed OWASP Budget tool (http://www.owasp.org/index.php/Conference_Budget_Planning_Tool)<br>Can either be uploaded to the Wiki or a Google Doc (with all owasp view permissions)", verify_exists=False, max_length=2048)

	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		
		content = super(RegionalEvent, self).TxtContent()
		content += "Proposal URL: "+self.pitch_url+"\n"
		content += "Budget URL: "+self.budget_url+"\n"
		content += "Admission Fee: "+str(self.admission_fee)+"\n"
		content += "Facility Cost: "+str(self.facility_cost)+ "\n"
		content += "Operations Cost: "+str(self.operations_cost)+"\n"
		content += "Speaking Slots: " + str(self.speaking_slots) +"\n"
		content += "Tracks: " + str(self.tracks) +"\n"
		content += "Training Classes: " + str(self.training_classes) +"\n"
		content += "Training Class size: " + str(self.training_class_size) +"\n"
		content += "Training Fee: "+str(self.training_admission_fee)+"\n"
		content += "\n\n\n\n"+settings.SITE_URL_ROOT+"/events/"+str(self.id)
		return content
	
class GlobalAppSecEvent(Event):
	class meta:
		verbose_name = "Global AppSec Event"
		verbose_name_plural = "Global AppSec Events"
		
	facility_cost = models.DecimalField("Facility Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated (or quoted) cost of the facility for the event")
	operations_cost = models.DecimalField("Operations Costs", default="0.00", max_digits=19, decimal_places=2, help_text="($000.00) Estimated cost for event operations, food, schwag et all")
	admission_fee = models.DecimalField("Admission Fee", default="250.00", max_digits=19, decimal_places=2, help_text="($000.00) Anticipated 'FULL PRICE' admission cost (Whole number).  Must be greater than $1", validators=[MinValueValidator(1)])
	speaking_slots = models.IntegerField("Number of Speaking Slots", default=1, help_text="Estimated total number of speaking slots")
	tracks = models.IntegerField("Number of Tracks", default=1, help_text="Planned number of speaking tracks at the event")		
	training_classes = models.IntegerField("Number of Training Classes", default=1, help_text="Planned number of training classes to be offered (Required for Global AppSec Events)", validators=[MinValueValidator(1)])
	training_class_size = models.IntegerField("Class Size", default=0, help_text="Estimated average number of students per training class")
	training_admission_fee = models.DecimalField("Training Fee", default="750.00", max_digits=19, decimal_places=2, help_text="($000.00) Anticipated one day training fee")
	pitch_url = models.URLField("Proposal Presentation Link", blank=True, help_text="Some events choose to put together a short presentation describing the organizing committee, previous conference experience, existing local supporting organizations, local attractions, access to transportation, local culture and more.  In general, these are presentations to 'pitch' your city to the committee.  Examples of successful presentations are on the help page. <br>Can either be uploaded to the Wiki or a Google Doc (with all owasp view permissions)", verify_exists=False, max_length=2048)
	budget_url = models.URLField("Budget Link", help_text="Must be a valid link to a completed OWASP Budget tool (http://www.owasp.org/index.php/Conference_Budget_Planning_Tool)<br>Can either be uploaded to the Wiki or a Google Doc (with all owasp view permissions)", max_length=2024)

	def TxtContent(self):
		'''
		Name: 		TxtContent(self)
		Arguments: 	self = reference to myself
		Return:		None
		Purpose: This function outputs information about the event in text form.	
		'''	
		
		content = super(GlobalAppSecEvent, self).TxtContent()
		content += "Proposal URL: "+self.pitch_url+"\n"
		content += "Budget URL: "+self.budget_url+"\n"
		content += "Admission Fee: "+str(self.admission_fee)+"\n"
		content += "Facility Cost: "+str(self.facility_cost)+ "\n"
		content += "Operations Cost: "+str(self.operations_cost)+"\n"
		content += "Speaking Slots: " + str(self.speaking_slots) +"\n"
		content += "Tracks: " + str(self.tracks) +"\n"
		content += "Training Classes: " + str(self.training_classes) +"\n"
		content += "Training Class size: " + str(self.training_class_size) +"\n"
		content += "Training Fee: "+str(self.training_admission_fee)+"\n"
		content += "\n\n\n\n"+settings.SITE_URL_ROOT+"/events/"+str(self.id)
		return content
	
class Vote(models.Model):
	event = models.ForeignKey(Event, verbose_name="Associated Event")
	user = models.ForeignKey(User)
	vote = models.CharField(max_length=1, default='A', choices=VOTE_CHOICES)
	vote_date=models.DateTimeField("Date Vote entered")

	def __unicode__(self):
		return self.vote
	
# There be dragons here.  This has to stay at the bottom of the page otherwise you creat a circular import situation.  Way to go python.	
#store integration
from ocms.merch.owasp_payments.models import OWASPPayment
